home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C++ / Applications / PICSee Dust 1.01 / Primary Source / PICSViewer.cpp < prev    next >
C/C++ Source or Header  |  1995-12-03  |  49KB  |  1,757 lines

  1. #include "PICSViewer.h"
  2. #include "PICSViewerPrivate.h"
  3. #include "PICS_Operations.h"
  4. #include "PICS_Utils.h"
  5.  
  6. #include "CropPICTs.h"                // For CapturePICT()
  7. #include "PICSCompositeDialog.h"    // For SetupCompositeDialog()
  8. #include "PICS_Split.h"
  9.  
  10. #include "Class_ProgressWindow.h"
  11. #include "GrayDraw 3D.h"
  12.  
  13. #include "Class_DynamoArray.cpp"
  14. #include "FileRegistry.h"
  15. #include "MovableModalDialogs.h"
  16.  
  17. #include "SimpleError.h"
  18. #include "QDUtils.h"
  19. #include "Monitors.h"
  20. #include "TimerUtils.h"
  21. #include "KeyUtils.h"
  22. #include "assert_mac.h"
  23.  
  24. #include "main.h"                    // For AppEmergencyUpdate()
  25.  
  26. // ---------------------------------------------------------------------------
  27.  
  28. static DynamoArray<PICSViewerData*> sViewerList;
  29.  
  30. static struct {
  31.     short changeCreator;
  32.     short animationMethod;
  33.     short appFileRefNum;
  34.     long minFileSize;
  35. } sPICSViewer;
  36.  
  37. // ---------------------------------------------------------------------------
  38.  
  39. void InitPICSViewer(Boolean changeCreator, short animationMethod, RGBColor *marqueeColor) {
  40.     PicHandle btnPic;
  41.     Rect drawRect;
  42.     Rect bufferBounds;
  43.     Rect monitorBounds;
  44.     GWorldPtr saveWorld;
  45.     GDHandle saveDev;
  46.     
  47.     sPICSViewer.changeCreator = changeCreator;
  48.     SetPICSViewerAnimationMethod(animationMethod);
  49.     SetPICSViewerMarqueeColor(marqueeColor);
  50.     sPICSViewer.appFileRefNum = CurResFile();
  51.  
  52.     btnPic = (PicHandle)Get1Resource('PICT', kPICSViewer_StopBtnUpID);
  53.     sBtnData.bounds = (**btnPic).picFrame;
  54.     FlushRectTopLeft(&sBtnData.bounds);
  55.     
  56.     bufferBounds = sBtnData.bounds;
  57.     bufferBounds.right = (kPICSViewer_ForwardBtn-kPICSViewer_StopBtn+1) *
  58.         bufferBounds.right;
  59.     bufferBounds.bottom += bufferBounds.bottom;
  60.     
  61.     GetGWorld(&saveWorld, &saveDev);
  62.     monitorBounds = (**GetDeepestDevice()).gdRect;
  63.     CenterRect(&bufferBounds, &monitorBounds);
  64.     
  65.     GBErr err;
  66.     /*
  67.     err = NewGraphicsBuffer(&sBtnData.buffer, 0,
  68.         (CP_Rect*)&bufferBounds, kGBOptimalFlag, 0);
  69.     FlushRectTopLeft(&bufferBounds);
  70.     */
  71.     FlushRectTopLeft(&bufferBounds);
  72.     err = NewGraphicsBuffer(&sBtnData.buffer, 8,
  73.         (CP_Rect*)&bufferBounds, kGBOptimalFlag, 0);
  74.     
  75.     SetGraphicsBuffer(sBtnData.buffer);
  76.     
  77.     short i;
  78.     for (i = kPICSViewer_StopBtnUpID; i <= kPICSViewer_ForwardBtnUpID; i++) {
  79.         btnPic = (PicHandle)Get1Resource('PICT', i);
  80.         drawRect = sBtnData.bounds;
  81.         OffsetRect(&drawRect, drawRect.right * (i-kPICSViewer_StopBtnUpID), 0);
  82.         DrawPicture(btnPic, &drawRect);
  83.         ReleaseResource((Handle)btnPic);
  84.     }
  85.     
  86.     for (i = kPICSViewer_StopBtnDownID; i <= kPICSViewer_ForwardBtnDownID; i++) {
  87.         btnPic = (PicHandle)Get1Resource('PICT', i);
  88.         drawRect = sBtnData.bounds;
  89.         OffsetRect(&drawRect, drawRect.right * (i-kPICSViewer_StopBtnDownID),
  90.             drawRect.bottom);
  91.         DrawPicture(btnPic, &drawRect);
  92.         ReleaseResource((Handle)btnPic);
  93.     }
  94.     
  95.     SetGWorld(saveWorld, saveDev);
  96. } // END InitPICSViewer
  97.  
  98. // ---------------------------------------------------------------------------
  99.  
  100. void SetPICSViewerAnimationMethod(short animationMethod) {
  101.     sPICSViewer.animationMethod = animationMethod;
  102. } // END SetPICSViewerAnimationMethod
  103.  
  104. void SetPICSViewerMarqueeColor(RGBColor *marqueeColor) {
  105.     _SetMarqueeColor(marqueeColor);
  106. } // END SetPICSViewerMarqueeColor
  107.  
  108. void SetPICSViewerOpenProgress(short minFileSizeToShow) {
  109.     sPICSViewer.minFileSize = minFileSizeToShow;
  110. } // END SetPICSViewerOpenProgress
  111.  
  112. // ---------------------------------------------------------------------------
  113.  
  114. void CleanupPICSViewer() {
  115.     if (sBtnData.buffer != NULL) {
  116.         DisposeGraphicsBuffer(sBtnData.buffer);
  117.     }
  118. } // END CleanupPICSViewer
  119.  
  120. // ---------------------------------------------------------------------------
  121.  
  122. void _DrawPICSViewerBtn(PICSViewerData *viewer, short btn, Boolean pushed) {
  123.     Rect srcRect, destRect;
  124.     short offset = btn - kPICSViewer_StopBtn;
  125.     RGBColor saveBack, backColor;
  126.  
  127.     GetBackColor(&saveBack);
  128.     backColor.red = backColor.green = backColor.blue = 0xFFFF;
  129.     RGBBackColor(&backColor);
  130.  
  131.     srcRect = sBtnData.bounds;
  132.     OffsetRect(&srcRect, srcRect.right * offset, pushed ? srcRect.bottom : 0);
  133.     
  134.     GetDItemRect(viewer->window, btn, &destRect);
  135.     
  136.     CopyGraphicsBuffer2Window(sBtnData.buffer, viewer->window,
  137.         (CP_Rect*)&srcRect, (CP_Rect*)&destRect);
  138.     
  139.     RGBBackColor(&saveBack);
  140. } // END DrawPICSViewerBtn
  141.  
  142. // ---------------------------------------------------------------------------
  143.  
  144. PICSViewerData *_ExtractPICSViewer(DialogPtr srcViewer) {
  145.     if (srcViewer != NULL) {
  146.         return((PICSViewerData*)GetWRefCon(srcViewer));
  147.     }
  148.     else
  149.         return(NULL);
  150. } // END ExtractPICSViewer
  151.  
  152. // ---------------------------------------------------------------------------
  153. // ---------------------------------------------------------------------------
  154.  
  155. DialogPtr GetCurrentPICSViewer() {
  156.     DialogPtr topWindow = FrontWindow();
  157.  
  158.     while (topWindow != NULL) {
  159.         if (GetWindowKind(topWindow) == dialogKind &&
  160.             !IsMovableModal(topWindow)) {
  161.             PICSViewerData *viewer;
  162.             viewer = _ExtractPICSViewer(topWindow);
  163.             ASSERT(viewer != NULL);
  164.             return(viewer->window);
  165.         }
  166.         topWindow = GetNextWindow(topWindow);
  167.     }
  168.     return(NULL);
  169. } // END GetCurrentPICSViewer
  170.  
  171. Boolean IsPICSViewer(DialogPtr viewer) {
  172.     PICSViewerData *realViewer;
  173.     realViewer = _ExtractPICSViewer(viewer);
  174.     if (realViewer == NULL)
  175.         return(false);
  176.  
  177.     if (sViewerList.Search(realViewer) != kDynamoArrayOutOfBoundsErr)
  178.         return(true);
  179.     else
  180.         return(false);
  181. } // END IsPICSViewer
  182.  
  183. Boolean ClosePICSViewer(DialogPtr viewer) {
  184.     return(_ClosePICSViewer(_ExtractPICSViewer(viewer)));
  185. }
  186.  
  187. Boolean SavePICSViewer(DialogPtr viewer) {
  188.     return(_SavePICSViewer(_ExtractPICSViewer(viewer)));
  189. }
  190.  
  191. void ClickPICSViewer(DialogPtr viewer, short itemClicked) {
  192.     _ClickPICSViewer(_ExtractPICSViewer(viewer), itemClicked);
  193. }
  194.  
  195. void KeyDownPICSViewer(DialogPtr viewer, char keyPressed) {
  196.     _KeyDownPICSViewer(_ExtractPICSViewer(viewer), keyPressed);
  197. }
  198.  
  199. void UpdatePICSViewer(DialogPtr viewer) {
  200.     _UpdatePICSViewer(_ExtractPICSViewer(viewer));
  201. }
  202.  
  203. void ActivatePICSViewer(DialogPtr viewer, Boolean activate) {
  204.     _ActivatePICSViewer(_ExtractPICSViewer(viewer), activate);
  205. }
  206.  
  207. void IdlePICSViewer(DialogPtr viewer) {
  208.     _IdlePICSViewer(_ExtractPICSViewer(viewer));
  209. }
  210.  
  211. Boolean IsPICSViewerModified(DialogPtr viewer) {
  212.     return(_IsPICSViewerModified(_ExtractPICSViewer(viewer)));
  213. }
  214.  
  215. void SelectEntirePICSViewerFrame(DialogPtr viewer, Boolean select) {
  216.     _SelectEntirePICSViewerFrame(_ExtractPICSViewer(viewer), select);
  217. }
  218.  
  219. void CopyPICSViewerFrame(DialogPtr viewer) {
  220.     _CopyPICSViewerFrame(_ExtractPICSViewer(viewer));
  221. }
  222.  
  223. void CompositePICSViewer(DialogPtr viewer) {
  224.     _ClickPICSViewer(_ExtractPICSViewer(viewer), kPICSViewer_CompositeBtn);
  225. } // END CompositePICSViewer
  226.  
  227. void SplitPICSViewer(DialogPtr viewer) {
  228.     _ClickPICSViewer(_ExtractPICSViewer(viewer), kPICSViewer_SplitBtn);
  229. } // END SplitPICSViewer
  230.  
  231. // ---------------------------------------------------------------------------
  232.  
  233. void NewPICSViewer(FSSpec *picsFile) {
  234.     PICSViewerData *viewer;
  235.  
  236.     // Get a PICS file first
  237.     FSSpec fileToOpen;
  238.     if (picsFile == NULL) {
  239.         if (!OpenPICSFile(&fileToOpen))
  240.             return;
  241.     }
  242.     else {
  243.         fileToOpen = *picsFile;
  244.  
  245.         // Make sure it's a PICS file...
  246.         FInfo fileInfo;
  247.         OSErr myErr;
  248.         myErr = FSpGetFInfo(&fileToOpen, &fileInfo);
  249.         if (fileInfo.fdType == kPICTFileType) {
  250.             SimpleError(kAlertErrID, kErrMsgID, kDontAcceptPICTFileErrMsg);
  251.             return;
  252.         }
  253.     }
  254.     
  255.     viewer = (PICSViewerData*)NewPtr(sizeof(PICSViewerData));
  256.     ASSERT(viewer != NULL);
  257.  
  258.     // Copy file spec of the PICS file to open
  259.     viewer->picsFile = fileToOpen;
  260.  
  261.     // Time to load 'em in
  262.     PicHandle thePic;
  263.     Rect bufferBounds;
  264.     Rect monitorBounds;
  265.     GWorldPtr saveWorld;
  266.     GDHandle saveDev;
  267.  
  268.     SetCursor(*GetCursor(watchCursor));
  269.  
  270.  
  271.     // -------------------------
  272.     // OPEN PICS FILE
  273.     // -------------------------
  274.     viewer->fileRefNum =
  275.         FSpOpenResFile(&viewer->picsFile, fsRdWrPerm);    // Exclusive read/write since we'll
  276.                                                         // keep it open for the duration...
  277.     if (viewer->fileRefNum == -1) {
  278.         SimpleError(kAlertErrID, kErrMsgID, kCantOpenFileErrMsg);
  279.         DisposePtr((Ptr)viewer);
  280.         return;
  281.     }
  282.     if (!RegisterFile(viewer->fileRefNum)) {
  283.         // File already opened by us. Find it and select it.        
  284.         WindowPtr theWindow = FrontWindow();
  285.         Boolean foundWindow = false;
  286.         while (theWindow != NULL) {
  287.             if (IsPICSViewer(theWindow)) {
  288.                 PICSViewerData *theViewer = _ExtractPICSViewer(theWindow);
  289.                 if (theViewer->fileRefNum == viewer->fileRefNum) {
  290.                     SelectWindow(theViewer->window);
  291.                     foundWindow = true;
  292.                 }
  293.             }
  294.             if (!foundWindow)
  295.                 theWindow = GetNextWindow(theWindow);
  296.             else
  297.                 theWindow = NULL;
  298.         }
  299.         DisposePtr((Ptr)viewer);
  300.         UseResFile(sPICSViewer.appFileRefNum);
  301.         return;
  302.     }
  303.  
  304.     // Send update event
  305.     AppEmergencyUpdate();
  306.     
  307.     UseResFile(viewer->fileRefNum);
  308.  
  309.     // ----------------------------------
  310.     // DETERMINE WHICH MONITOR TO USE
  311.     // ----------------------------------
  312.     GDHandle outputDevice = GetDeepestDevice();
  313.     short monitorDepth = GetDeviceDepth(outputDevice);
  314.     short bufferDepth;
  315.     GetGWorld(&saveWorld, &saveDev);
  316.     monitorBounds = (**outputDevice).gdRect;
  317.  
  318.  
  319.     // ----------------------------------
  320.     // SETUP PROGRESS WINDOW
  321.     // ----------------------------------
  322.     Boolean doProgress;
  323.     ProgressWindow *progressWindow = NULL;
  324.     short increment;
  325.     long fileSize;
  326.     
  327.     doProgress = false;
  328.     if (sPICSViewer.minFileSize == kAlwaysShowOpenProgress) {
  329.         doProgress = true;
  330.     }
  331.     else if (sPICSViewer.minFileSize > 0) {
  332.         (void)GetEOF(viewer->fileRefNum, &fileSize);
  333.         // minFileSize is in Kbytes, fileSize is in bytes
  334.         if ((fileSize/1024) >= sPICSViewer.minFileSize)
  335.             doProgress = true;
  336.     }
  337.     
  338.     if (doProgress) {
  339.         progressWindow = new ProgressWindow(outputDevice, 100);
  340.         SetPort(progressWindow->GetWindow());
  341.         TextFont(geneva);
  342.         TextSize(9);
  343.         TextFace(bold);
  344.     }
  345.  
  346.     // -------------------------
  347.     // GET NUMBER OF FRAMES
  348.     // -------------------------
  349.     if (doProgress) {
  350.         progressWindow->SetPrimaryMessage("\pCounting number of frames...");
  351.     }
  352.     viewer->numFrames = Count1Resources(kPICSRsrcType);
  353.     if (viewer->numFrames < 1) {
  354.         if (doProgress) {
  355.             delete progressWindow;
  356.         }
  357.         UseResFile(sPICSViewer.appFileRefNum);
  358.         SimpleError(kAlertErrID, kErrMsgID, kNoPICTsInFileErrMsg);
  359.         (void)UnregisterFile(viewer->fileRefNum);
  360.         CloseResFile(viewer->fileRefNum);
  361.         viewer->fileRefNum = -1;
  362.         DisposePtr((Ptr)viewer);
  363.         return;
  364.     }
  365.     if (doProgress) {
  366.         increment = 100 / (viewer->numFrames+6);
  367.     }
  368.  
  369.  
  370.     // ----------------------------------
  371.     // GET DEPTH AND AREA OF PICT
  372.     // ----------------------------------
  373.     if (doProgress) {
  374.         progressWindow->SetPrimaryMessage("\pGetting depth and boundary...");
  375.         progressWindow->IncrementBy(increment);
  376.     }
  377.     thePic = (PicHandle)Get1Resource(kPICSRsrcType, 128);
  378.     if (thePic == NULL) {
  379.         // We have picts in the file, but there isn't one
  380.         // with an id of 128! Hmm. Probably not a PICS file.
  381.         if (doProgress) {
  382.             delete progressWindow;
  383.         }
  384.         UseResFile(sPICSViewer.appFileRefNum);
  385.         SimpleError(kAlertErrID, kErrMsgID, kInvalidPICSFormatErrMsg);
  386.         (void)UnregisterFile(viewer->fileRefNum);
  387.         CloseResFile(viewer->fileRefNum);
  388.         viewer->fileRefNum = -1;
  389.         DisposePtr((Ptr)viewer);
  390.         return;
  391.     }
  392.     viewer->picsInfo.depth = GetPictDepth(thePic);
  393.     
  394.     if (doProgress) {
  395.         progressWindow->IncrementBy(increment);
  396.     }
  397.     short offsetH, offsetV;
  398.     viewer->picsFrame = (**thePic).picFrame;
  399.     viewer->outputFrame = (**thePic).picFrame;
  400.     // Just in case the topLeft of the picture ISN'T (0,0), we'll
  401.     // store it for later safekeeping
  402.     offsetH = viewer->picsFrame.left;
  403.     offsetV = viewer->picsFrame.top;
  404.     // Now make it flushed topLeft (0,0)
  405.     FlushRectTopLeft(&viewer->picsFrame);
  406.  
  407.     
  408.     // ----------------------------------
  409.     // DETERMINE AREA OF BUFFER
  410.     // ----------------------------------
  411.     if (doProgress) {
  412.         progressWindow->SetPrimaryMessage("\pDetermining memory boundary...");
  413.         progressWindow->IncrementBy(increment);
  414.     }
  415.     bufferBounds = viewer->picsFrame;
  416.     bufferBounds.bottom = (viewer->numFrames) *
  417.         bufferBounds.bottom;
  418.         
  419.     
  420.     // ----------------------------------
  421.     // SETUP ANIMATION METHOD
  422.     // ----------------------------------
  423.     if (doProgress) {
  424.         progressWindow->SetPrimaryMessage("\pSetting up memory...");
  425.         progressWindow->IncrementBy(increment);
  426.     }
  427.     viewer->animationMethod = sPICSViewer.animationMethod;
  428.     if (REVERSEANIMATION_SHORTCUT) {
  429.         if (viewer->animationMethod == kUsePictureGWorldMethod)
  430.             viewer->animationMethod = kUseMonitorGWorldMethod;
  431.         else if (viewer->animationMethod == kUseMonitorGWorldMethod)
  432.             viewer->animationMethod = kUsePictureGWorldMethod;
  433.     }
  434.     if (viewer->animationMethod == kUsePictureGWorldMethod) {
  435.         // Specify an explicit depth only if depth of PICS isn't
  436.         // same as that of the screen
  437.         if (monitorDepth != viewer->picsInfo.depth) {
  438.             bufferDepth = viewer->picsInfo.depth;
  439.             // Use local coords (topleft is 0,0)
  440.         }
  441.         else {
  442.             bufferDepth = 0;
  443.             CenterRect(&bufferBounds, &monitorBounds);
  444.             // Use global coords
  445.         }
  446.     }
  447.     else if (viewer->animationMethod == kUseMonitorGWorldMethod) {
  448.         bufferDepth = 0;
  449.         CenterRect(&bufferBounds, &monitorBounds);
  450.     }
  451.  
  452.  
  453.     // -------------------------
  454.     // ALLOCATE GRAPHICSBUFFER
  455.     // -------------------------
  456.     if (doProgress) {
  457.         progressWindow->SetPrimaryMessage("\pAllocating memory...");
  458.         progressWindow->IncrementBy(increment);
  459.     }
  460.     GBErr err;
  461.     err = NewGraphicsBuffer(&viewer->buffer, bufferDepth,
  462.         (CP_Rect*)&bufferBounds, kGBOptimalFlag, 0);
  463.     if (err != noErr) {
  464.         if (viewer->animationMethod == kUseMonitorGWorldMethod &&
  465.             viewer->picsInfo.depth < monitorDepth &&
  466.             HasMonitorDepth(outputDevice, viewer->picsInfo.depth, true)) {
  467.             // We're using expensive method. Try less memory
  468.             // intensive kUsePictureGWorldMethod.
  469.             viewer->animationMethod = kUsePictureGWorldMethod;
  470.             bufferDepth = viewer->picsInfo.depth;
  471.             FlushRectTopLeft(&bufferBounds);
  472.             err = NewGraphicsBuffer(&viewer->buffer, bufferDepth,
  473.                 (CP_Rect*)&bufferBounds, kGBOptimalFlag, 0);
  474.             if (err != noErr) {
  475.                 // Aack! Still not enough memory. Oh well...
  476.                 if (doProgress) {
  477.                     delete progressWindow;
  478.                 }
  479.                 UseResFile(sPICSViewer.appFileRefNum);
  480.                 SimpleError(kAlertErrID, kErrMsgID, kNoGWorldErrMsg);
  481.                 (void)UnregisterFile(viewer->fileRefNum);
  482.                 CloseResFile(viewer->fileRefNum);
  483.                 viewer->fileRefNum = -1;
  484.                 DisposePtr((Ptr)viewer);
  485.                 return;
  486.             }
  487.         }
  488.         else {
  489.             if (doProgress) {
  490.                 delete progressWindow;
  491.             }
  492.             UseResFile(sPICSViewer.appFileRefNum);
  493.             SimpleError(kAlertErrID, kErrMsgID, kNoGWorldErrMsg);
  494.             (void)UnregisterFile(viewer->fileRefNum);
  495.             CloseResFile(viewer->fileRefNum);
  496.             viewer->fileRefNum = -1;
  497.             DisposePtr((Ptr)viewer);
  498.             return;
  499.         }
  500.     }
  501.     FlushRectTopLeft(&bufferBounds);
  502.     if (bufferDepth != 0)
  503.         viewer->bufferDepth = bufferDepth;
  504.     else {
  505.         viewer->bufferDepth = monitorDepth;
  506.     }
  507.  
  508.  
  509.     // ----------------------------------
  510.     // DRAW PICTS INTO BUFFER
  511.     // ----------------------------------
  512.     if (doProgress) {
  513.         progressWindow->IncrementBy(increment);
  514.     }
  515.     short offsetAmount;
  516.     Rect drawRect;
  517.     Str15 countFrameStr;
  518.  
  519.     viewer->usesDeltaPictures = false;
  520.  
  521.     if (doProgress) {
  522.         progressWindow->SetPrimaryMessage("\pLoading frame:");
  523.     }
  524.  
  525.     for (short i = 1, resID = kPICSRsrcStartID;
  526.         i <= viewer->numFrames; i++, resID++) {
  527.  
  528.         // Set to old gworld & window port & draw message
  529.         if (doProgress) {
  530.             SetGWorld(saveWorld, saveDev);
  531.             SetPort(progressWindow->GetWindow());
  532.             NumToString(i, countFrameStr);
  533.             progressWindow->SetSecondaryMessage(countFrameStr);
  534.         }
  535.  
  536.         // Set to offscreen buffer
  537.         SetGraphicsBuffer(viewer->buffer);
  538.         thePic = (PicHandle)Get1Resource(kPICSRsrcType, resID);
  539.  
  540.         if (thePic == NULL) {
  541.             if (doProgress) {
  542.                 delete progressWindow;
  543.             }
  544.             UseResFile(sPICSViewer.appFileRefNum);
  545.             SimpleError(kAlertErrID, kErrMsgID, kUnableLoadPICTErrMsg);
  546.             (void)UnregisterFile(viewer->fileRefNum);
  547.             CloseResFile(viewer->fileRefNum);
  548.             viewer->fileRefNum = -1;
  549.             DisposeGraphicsBuffer(viewer->buffer);
  550.             viewer->buffer = NULL;
  551.             DisposePtr((Ptr)viewer);
  552.             return;
  553.         }
  554.  
  555.         /*
  556.             Here it gets tricky, because the PICS format is somewhat
  557.             "goofy": there are actually 2 ways a PICS can be made:
  558.             The first way, and the easiest, is a sequential series of
  559.             PICTs which are all the size dimensions. Very straightforward.
  560.             The second way is to have a first picture which defines
  561.             the rest of the frames. The rest of the frames are defined
  562.             by changes to the previous frame; thus they will have
  563.             different frame rects. These are called delta pictures.
  564.             [History: the PICS format is old, and memory conservation
  565.             was much more important then than now, and delta pictures
  566.             served a way to do this. However, it now complicates my
  567.             life, since one program that I use implements this method:
  568.             Cinemation. Aargh!]
  569.             
  570.             We'll look for delta pictures by looking at the picture's
  571.             picFrame. If it's smaller than that of the first picture,
  572.             it's a delta picture...
  573.         */
  574.  
  575.         // Get frame of first picture
  576.         drawRect = viewer->picsFrame;
  577.         // Offset it appropriately into viewer->buffer
  578.         offsetAmount = drawRect.bottom * (i-1);
  579.         
  580.         if (i > 1) {
  581.             Rect picFrame = (**thePic).picFrame;
  582.  
  583.             // If first picture wasn't flushed topLeft (0,0), we
  584.             // have to do the same compensation here
  585.             OffsetRect(&picFrame, -offsetH, -offsetV);
  586.  
  587.             if (picFrame.left > drawRect.left ||
  588.                 picFrame.top > drawRect.top ||
  589.                 picFrame.right < drawRect.right ||
  590.                 picFrame.bottom < drawRect.bottom) {
  591.                 // Shucks! Delta pictures...
  592.                 // Copy previous frame
  593.                 Rect prevFrameRect = viewer->picsFrame;
  594.                 Rect curFrameRect = drawRect;
  595.                 OffsetRect(&prevFrameRect, 0, prevFrameRect.bottom * (i-2));
  596.                 OffsetRect(&curFrameRect, 0, offsetAmount);
  597.                 CopyGraphicsBuffer(viewer->buffer, viewer->buffer,
  598.                     (CP_Rect*)&prevFrameRect, (CP_Rect*)&curFrameRect);
  599.                 if (VIEWDELTA_DEBUG)
  600.                     FillRect(&curFrameRect, &qd.gray);
  601.                 // Adjust destination rect for later DrawPicture()
  602.                 // because of delta picture
  603.                 drawRect = picFrame;
  604.                 viewer->usesDeltaPictures = true;
  605.             }
  606.         }
  607.  
  608.         OffsetRect(&drawRect, 0, offsetAmount);
  609.         DrawPicture(thePic, &drawRect);
  610.         ReleaseResource((Handle)thePic);
  611.  
  612.         if (doProgress) {
  613.             SetGWorld(saveWorld, saveDev);    // we have to do this otherwise progress isn't shown
  614.             SetPort(progressWindow->GetWindow());
  615.             progressWindow->IncrementBy(increment);
  616.         }
  617.     }
  618.  
  619.  
  620.     // ----------------------------------
  621.     // GET PICS SPEED ('INFO')
  622.     // ----------------------------------
  623.     SetGWorld(saveWorld, saveDev);
  624.     if (doProgress) {
  625.         SetPort(progressWindow->GetWindow());
  626.         progressWindow->SetPrimaryMessage("\pGetting PICS speed...");
  627.         progressWindow->SetSecondaryMessage(NULL);
  628.     }
  629.     PICSInfoRsrcHdl picsInfo;
  630.  
  631.     picsInfo = GetPICSInfo();
  632.     if (picsInfo != NULL) {
  633.         viewer->picsInfo = **picsInfo;
  634.         DisposePICSInfo(picsInfo);
  635.     }
  636.     else {
  637.         // Currently the only thing we use from the PICSInfo
  638.         viewer->picsInfo.speed = kDefaultPICSSpeed;
  639.     }
  640.  
  641.  
  642.  
  643.     SetGWorld(saveWorld, saveDev);
  644.     if (doProgress) {
  645.         SetPort(progressWindow->GetWindow());
  646.         progressWindow->SetPrimaryMessage("\pFinishing up...");
  647.         progressWindow->FinishProgress();
  648.         delete progressWindow;
  649.         FlushEvents(everyEvent, 0);
  650.     }
  651.  
  652.     // ----------------------------------
  653.     // CREATE PICSVIEWER WINDOW
  654.     // ----------------------------------
  655.     UseResFile(sPICSViewer.appFileRefNum);
  656.  
  657.     short dialogID = kPICSViewerTemplateID;
  658.     if (viewer->picsFrame.right > kSmallWidth ||
  659.         viewer->picsFrame.bottom > kSmallHeight ||
  660.         USELARGEWINDOW_SHORTCUT) {
  661.         dialogID = kPICSViewerTemplateID+1; // Large dialog version
  662.     }
  663.     viewer->window = GetNewDialog(dialogID, NULL, (WindowPtr)-1);
  664.     SetWTitle(viewer->window, viewer->picsFile.name);
  665.     ASSERT(viewer->window != NULL);
  666.     // Stuff reference to PICSViewer inside refcon of dialog
  667.     SetWRefCon(viewer->window, (long)viewer);
  668.     SetPort(viewer->window);
  669.     TextFont(geneva);
  670.     TextSize(9);
  671.  
  672.  
  673.     // ----------------------------------
  674.     // SET PICSVIEWER PARAMETERS
  675.     // ----------------------------------
  676.     Str15 speedStr;
  677.     Rect frame;
  678.  
  679.     viewer->curFrame = 0;
  680.     viewer->doLoop = true;
  681.     viewer->doBounceBack = false;
  682.     viewer->trackMouse = true;
  683.     SetRect(&viewer->marqueeRect, 0, 0, 0, 0);
  684.     SetPt(&viewer->trackingPoint, 0, 0);
  685.     viewer->currentAction = kSelectionAction;
  686.     viewer->magnification = kNormalMagnification;
  687.     viewer->fileModified = false;
  688.     (void)GetEOF(viewer->fileRefNum, &viewer->fileSize);
  689.     
  690.     // Make sure speed is valid (could be negative from an
  691.     // old version of 'INFO')
  692.     if (viewer->picsInfo.speed < 1) {
  693.         viewer->picsInfo.speed = kDefaultPICSSpeed;
  694.         viewer->fileModified = true;
  695.     }
  696.  
  697.     NumToString(viewer->picsInfo.speed, speedStr);
  698.     SetDItemText(viewer->window, kPICSViewer_SpeedPopupBtn, speedStr);
  699.     GetDItemRect(viewer->window, kPICSViewer_OutputBox, &frame);
  700.     CenterRect(&viewer->outputFrame, &frame);
  701.     SetDlogCtlValue(viewer->window, kPICSViewer_LoopBtn, 1);
  702.     SetDlogCtlValue(viewer->window, kPICSViewer_TrackMouseBtn, 1);
  703.     // Empty out track mouse field
  704.     SetDItemText(viewer->window, kPICSViewer_TrackMouseField, kEmptyStr);
  705.     SetDItemText(viewer->window, kPICSViewer_TrackCropField, kEmptyStr);
  706.  
  707.  
  708.     // ----------------------------------
  709.     // WE'RE DONE!!! (ALMOST)
  710.     // ----------------------------------
  711.     sViewerList.Append(viewer);    // Add viewer to list of windows
  712.     UseResFile(sPICSViewer.appFileRefNum);
  713.  
  714.     // Place window appropriately, depending whether there is already
  715.     // another window present (if so, stack new window)
  716.     WindowPtr topWindow = FrontWindow();
  717.     Rect globRect;
  718.  
  719.     if (topWindow == NULL) {
  720.         globRect = (**GetDeepestDevice()).gdRect;
  721.         OffsetRect(&globRect, 10, 40);
  722.         
  723.         MoveWindow(viewer->window, globRect.left, globRect.top, false);
  724.     }
  725.     else {
  726.         globRect = topWindow->portRect;
  727.         SetPort(topWindow);
  728.         LocalToGlobal(&topLeft(globRect));
  729.         LocalToGlobal(&botRight(globRect));
  730.         
  731.         MoveWindow(viewer->window, globRect.left + 20, globRect.top + 20, false);
  732.     }
  733.     ShowWindow(viewer->window);
  734.     SetPort(viewer->window);
  735.     SetCursor(&qd.arrow);
  736. } // END NewPICSViewer
  737.  
  738. // ---------------------------------------------------------------------------
  739.  
  740. Boolean _ClosePICSViewer(PICSViewerData *viewer) {
  741.     ASSERT(viewer != NULL);
  742.     
  743.     if (viewer == NULL)
  744.         return(false);
  745.  
  746.     Boolean doSave;
  747.     if (viewer->fileModified) {
  748.         ParamText(viewer->picsFile.name, kEmptyStr, kEmptyStr, kEmptyStr);
  749.         short itemHit = Alert(kPICSViewerPromptSaveID, NULL);
  750.         switch(itemHit) {
  751.             case kPICSViewerPromptSave:        doSave = true;    break;
  752.             case kPICSViewerPromptCancel:    return(false);    break;
  753.             case kPICSViewerPromptDontSave:    doSave = false;    break;
  754.         }
  755.     }
  756.  
  757.     SetCursor(*GetCursor(watchCursor));
  758.  
  759.     if (doSave) {
  760.         (void)_SavePICSViewer(viewer);
  761.     }
  762.  
  763.     HideWindow(viewer->window);
  764.     DisposeDialog(viewer->window);
  765.     viewer->window = NULL;
  766.     
  767.     if (viewer->buffer != NULL) {
  768.         DisposeGraphicsBuffer(viewer->buffer);
  769.     }
  770.     SetCursor(&qd.arrow);
  771.  
  772.     (void)UnregisterFile(viewer->fileRefNum);
  773.     CloseResFile(viewer->fileRefNum);
  774.     viewer->fileRefNum = -1;
  775.     UseResFile(sPICSViewer.appFileRefNum);
  776.  
  777.     DisposePtr((Ptr)viewer);
  778.     sViewerList.SearchDelete(viewer);    // Remove viewer from list of windows
  779.     return(true);
  780. } // END _ClosePICSViewer
  781.  
  782. // ---------------------------------------------------------------------------
  783.  
  784. Boolean _SavePICSViewer(PICSViewerData *viewer) {
  785.     if (viewer->fileModified) {        
  786.         UseResFile(viewer->fileRefNum);
  787.  
  788.         PICSInfoRsrcHdl picsInfo;
  789.         picsInfo = GetPICSInfo();
  790.  
  791.         if (picsInfo == NULL) {
  792.             // No PICS info exists. Create one
  793.             picsInfo = NewPICSInfoResource();
  794.             (**picsInfo).speed = viewer->picsInfo.speed;
  795.             (**picsInfo).depth = viewer->picsInfo.depth;
  796.             (**picsInfo).creatorType = kPICSiliciousCreatorType;
  797.         }
  798.         else {
  799.             if ((**picsInfo).speed != viewer->picsInfo.speed) {
  800.                 // Update speed parameter
  801.                 (**picsInfo).speed = viewer->picsInfo.speed;
  802.             }
  803.         }
  804.  
  805.         if (!SavePICSInfo(picsInfo, true)) {
  806.             UseResFile(sPICSViewer.appFileRefNum);
  807.             SimpleError(kAlertErrID, kErrMsgID, kCantSavePICSInfoErrMsg);
  808.             UseResFile(viewer->fileRefNum);
  809.         }
  810.         DisposePICSInfo(picsInfo);
  811.  
  812.         UseResFile(sPICSViewer.appFileRefNum);
  813.         
  814.         if (sPICSViewer.changeCreator) {
  815.             FInfo fileInfo;
  816.             OSErr myErr;
  817.             myErr = FSpGetFInfo(&viewer->picsFile, &fileInfo);
  818.             if (myErr == noErr &&
  819.                 fileInfo.fdCreator != kPICSiliciousCreatorType) {
  820.                 fileInfo.fdCreator = kPICSiliciousCreatorType;
  821.                 myErr = FSpSetFInfo(&viewer->picsFile, &fileInfo);
  822.             }
  823.         }
  824.         
  825.         _SetPICSViewerModified(viewer, false);
  826.     }
  827.     
  828.     return(true);
  829. } // END _SavePICSViewer
  830.  
  831. // ---------------------------------------------------------------------------
  832.  
  833. void _ClickPICSViewer(PICSViewerData *viewer, short itemClicked) {
  834.  
  835.     // Handle mouse in-out niceties
  836.     if (itemClicked >= kPICSViewer_StopBtn &&
  837.         itemClicked <= kPICSViewer_ForwardBtn) {
  838.         Rect btnRect;
  839.         Point mouseLoc;
  840.         
  841.         GetDItemRect(viewer->window, itemClicked, &btnRect);
  842.         _DrawPICSViewerBtn(viewer, itemClicked, true);
  843.         while (StillDown()) {}
  844.         if (itemClicked != kPICSViewer_PlayBtn)
  845.             _DrawPICSViewerBtn(viewer, itemClicked, false);
  846.  
  847.         SetPort(viewer->window);
  848.         GetMouse(&mouseLoc);
  849.         if (!PtInRect(mouseLoc, &btnRect)) {
  850.             if (itemClicked == kPICSViewer_PlayBtn)
  851.                 _DrawPICSViewerBtn(viewer, itemClicked, false);
  852.             return;
  853.         }
  854.     }
  855.  
  856.     switch(itemClicked) {
  857.         case kPICSViewer_StopBtn:
  858.         break;
  859.  
  860.         case kPICSViewer_RewindBtn:
  861.             _RewindPICSViewer(viewer);
  862.         break;
  863.  
  864.         case kPICSViewer_StepBackBtn:
  865.             _StepBackPICSViewer(viewer);
  866.         break;
  867.         
  868.         case kPICSViewer_PlayBtn:
  869.             _PlaySynchPICSViewer(viewer);
  870.         break;
  871.         
  872.         case kPICSViewer_StepForwardBtn:
  873.             _StepForwardPICSViewer(viewer);
  874.         break;
  875.  
  876.         case kPICSViewer_ForwardBtn:
  877.             _ForwardPICSViewer(viewer);
  878.         break;
  879.         
  880.         case kPICSViewer_SpeedField:
  881.         case kPICSViewer_SpeedPopupBtn:
  882.             MenuHandle speedMenu;
  883.             Rect popRect;
  884.             long menuResult;
  885.             Point whatsThePoint;
  886.             Str15 speedStr;
  887.             short itemToCheck;
  888.  
  889.             speedMenu = GetMenu(kPICSViewerSpeedMenuID);
  890.             ASSERT(speedMenu != NULL);
  891.             InsertMenu(speedMenu, -1);
  892.             GetDItemRect(viewer->window, kPICSViewer_SpeedField, &popRect);
  893.             whatsThePoint.h = popRect.right;
  894.             whatsThePoint.v = popRect.top;
  895.             SetPort(viewer->window);
  896.             LocalToGlobal(&whatsThePoint);
  897.  
  898.             // Determine which menu item to pre-checkmark, if any
  899.             itemToCheck = CountMItems(speedMenu);
  900.             for (short i = 1; i <= CountMItems(speedMenu); i++) {
  901.                 GetItem(speedMenu, i, speedStr);
  902.                 StringToNum(speedStr, &menuResult);
  903.                 if (viewer->picsInfo.speed == menuResult) {
  904.                     itemToCheck = i;
  905.                     SetItemMark(speedMenu, itemToCheck, '•');
  906.                     break;
  907.                 }
  908.             }
  909.  
  910.             menuResult = PopUpMenuSelect(speedMenu,
  911.                 whatsThePoint.v, whatsThePoint.h, itemToCheck);
  912.  
  913.             if (menuResult != 0) {
  914.                 GetItem(speedMenu, LoWord(menuResult), speedStr);
  915.                 StringToNum(speedStr, &menuResult);
  916.                 // If use did change speed, mark file as modified
  917.                 if (viewer->fileModified == false) {
  918.                     if (viewer->picsInfo.speed != menuResult)
  919.                         _SetPICSViewerModified(viewer, true);
  920.                 }
  921.                 viewer->picsInfo.speed = menuResult;
  922.                 _UpdatePICSViewerInfo(viewer);
  923.             }
  924.  
  925.             DeleteMenu(kPICSViewerTemplateID);
  926.             ReleaseResource((Handle)speedMenu);
  927.         break;
  928.         
  929.         case kPICSViewer_SpeedUpBtn:
  930.             _IncreasePICSViewerSpeed(viewer);
  931.         break;
  932.         
  933.         case kPICSViewer_SpeedDownBtn:
  934.             _DecreasePICSViewerSpeed(viewer);
  935.         break;
  936.  
  937.         case kPICSViewer_LoopBtn:
  938.             FlipCtlValue(viewer->window, itemClicked);
  939.             viewer->doLoop = GetDlogCtlValue(viewer->window, itemClicked);
  940.             if (viewer->doLoop)
  941.                 EnableDlogCtl(viewer->window, kPICSViewer_BounceBackBtn);
  942.             else
  943.                 DisableDlogCtl(viewer->window, kPICSViewer_BounceBackBtn);
  944.         break;
  945.         
  946.         case kPICSViewer_BounceBackBtn:
  947.             FlipCtlValue(viewer->window, itemClicked);
  948.             viewer->doBounceBack = GetDlogCtlValue(viewer->window, itemClicked);
  949.         break;
  950.  
  951.         case kPICSViewer_CompositeBtn:
  952.         case kPICSViewer_SplitBtn:
  953.             if (itemClicked == kPICSViewer_CompositeBtn) {
  954.                 _CompositePICSViewer(viewer);
  955.             }
  956.             else {
  957.                 _SplitPICSViewer(viewer);
  958.             }
  959.         break;
  960.         
  961.         case kPICSViewer_TrackMouseBtn:
  962.             FlipCtlValue(viewer->window, itemClicked);
  963.             viewer->trackMouse = GetDlogCtlValue(viewer->window, itemClicked);
  964.             if (!viewer->trackMouse) {
  965.                 SetDItemText(viewer->window,
  966.                     kPICSViewer_TrackMouseField, kEmptyStr);
  967.                 SetDItemText(viewer->window,
  968.                     kPICSViewer_TrackCropField, kEmptyStr);
  969.                 // And erase the current marquee
  970.                 if (!EmptyRect(&viewer->marqueeRect)) {
  971.                     _EraseMarquee(viewer, NULL);
  972.                     SetRect(&viewer->marqueeRect, 0, 0, 0, 0);
  973.                 }
  974.             }
  975.         break;
  976.  
  977.         case kPICSViewer_OutputBox:
  978.             _ActionPICSViewer(viewer);
  979.         break;
  980.  
  981.         case kPICSViewer_PopupInfoBtn:
  982.         /*
  983.         case kPICSViewer_CurFrameField:
  984.         case kPICSViewer_NumFramesField:
  985.         case kPICSViewer_PICSWidthField:
  986.         case kPICSViewer_PICSHeightField:
  987.         case kPICSViewer_TrackMouseField:
  988.         case kPICSViewer_TrackCropField:
  989.         */
  990.         //default:
  991.             _ShowPICSViewerPopupInfo(viewer);
  992.         break;    
  993.     }
  994. } // END ClickPICSViewer
  995.  
  996. // ---------------------------------------------------------------------------
  997.  
  998. enum {
  999.     kLineHeight = 12
  1000. };
  1001.  
  1002. void _ShowPICSViewerPopupInfo(PICSViewerData *viewer) {
  1003.     Rect popRect;
  1004.     Str15 numStr;
  1005.     short leftCol, rightCol;
  1006.     short linePos;
  1007.     
  1008.     GetDItemRect(viewer->window, kPICSViewer_OutputBox, &popRect);
  1009.     //popRect.left = popRect.right - 200;
  1010.     popRect.top = popRect.bottom - 120;
  1011.     popRect.right = popRect.left + 220;
  1012.     
  1013.     SetPort(viewer->window);
  1014.     OffsetRect(&popRect, 4, -4);
  1015.     EraseRect(&popRect);
  1016.     GrayDrawShadowBox(&popRect, NULL);
  1017.     
  1018.     // Anim buffer depth
  1019.     leftCol = popRect.left + 10;
  1020.     rightCol = leftCol + 130;
  1021.     linePos = popRect.top + 20;
  1022.  
  1023.     MoveTo(leftCol, linePos);
  1024.     DrawString("\pAnimation buffer depth:");
  1025.     NumToString(viewer->bufferDepth, numStr);
  1026.     MoveTo(rightCol, linePos);
  1027.     DrawString(numStr);
  1028.     DrawString("\p-bit");
  1029.  
  1030.     // Anim buffer size
  1031.     linePos += kLineHeight;
  1032.     Rect animArea;
  1033.     long bufferSize;
  1034.     GetGraphicsBufferBounds(viewer->buffer, (CP_Rect*)&animArea);
  1035.     FlushRectTopLeft(&animArea);
  1036.     bufferSize = GetGraphicsBufferRowBytes(viewer->buffer) *
  1037.         (animArea.bottom) / 1024;
  1038.     MoveTo(leftCol, linePos);
  1039.     DrawString("\pAnimation buffer size:");
  1040.     MoveTo(rightCol, linePos);
  1041.     NumToString(bufferSize, numStr);
  1042.     DrawString(numStr);
  1043.     DrawString("\pK");
  1044.  
  1045.     // Anim method
  1046.     linePos += kLineHeight;
  1047.     MoveTo(leftCol, linePos);
  1048.     DrawString("\pAnimation method:");
  1049.     MoveTo(rightCol, linePos);
  1050.     if (viewer->animationMethod == kUseMonitorGWorldMethod)
  1051.         DrawString("\pMonitor depth");
  1052.     else if (viewer->animationMethod == kUsePictureGWorldMethod)
  1053.         DrawString("\pFile depth");
  1054.  
  1055.  
  1056.     // File depth
  1057.     linePos += kLineHeight + (kLineHeight/2);
  1058.     MoveTo(leftCol, linePos);
  1059.     DrawString("\pPICS File depth:");
  1060.     NumToString(viewer->picsInfo.depth, numStr);
  1061.     MoveTo(rightCol, linePos);
  1062.     DrawString(numStr);
  1063.     DrawString("\p-bit");
  1064.  
  1065.     // File size
  1066.     linePos += kLineHeight;
  1067.     MoveTo(leftCol, linePos);
  1068.     DrawString("\pPICS File size:");
  1069.     NumToString(viewer->fileSize/1024, numStr);
  1070.     MoveTo(rightCol, linePos);
  1071.     DrawString(numStr);
  1072.     DrawString("\pK");
  1073.  
  1074.     // File creator type
  1075.     linePos += kLineHeight;
  1076.     MoveTo(leftCol, linePos);
  1077.     DrawString("\pInternal PICS creator type:");
  1078.     MoveTo(rightCol, linePos);
  1079.     if ((long)viewer->picsInfo.creatorType == 0)
  1080.         DrawString("\p<none>");
  1081.     else {
  1082.         numStr[0] = 4;
  1083.         numStr[1] = ((char*)&viewer->picsInfo.creatorType)[0];
  1084.         numStr[2] = ((char*)&viewer->picsInfo.creatorType)[1];
  1085.         numStr[3] = ((char*)&viewer->picsInfo.creatorType)[2];
  1086.         numStr[4] = ((char*)&viewer->picsInfo.creatorType)[3];
  1087.         DrawString(numStr);
  1088.     }
  1089.  
  1090.     // Delta picture info
  1091.     linePos += kLineHeight + (kLineHeight/2);
  1092.     MoveTo(leftCol, linePos);
  1093.     DrawString("\pUses delta picture method:");
  1094.     MoveTo(rightCol, linePos);
  1095.     if (viewer->usesDeltaPictures)
  1096.         DrawString("\pYes");
  1097.     else
  1098.         DrawString("\pNo");
  1099.  
  1100.     while (StillDown()) {
  1101.     }
  1102.     
  1103.     //_UpdatePICSViewer(viewer);
  1104.     EraseRect(&popRect);
  1105.     InvalRect(&popRect);
  1106. } // END _ShowPICSViewerPopupInfo
  1107.  
  1108. // ---------------------------------------------------------------------------
  1109.  
  1110. void _KeyDownPICSViewer(PICSViewerData *viewer, char keyPressed) {
  1111.     switch(keyPressed) {
  1112.         case kEscape_Key:
  1113.             _ClosePICSViewer(viewer);
  1114.         break;
  1115.         
  1116.         case kDelete_Key:
  1117.         break;
  1118.         
  1119.         case kTab_Key:
  1120.             _PlaySynchPICSViewer(viewer);
  1121.         break;
  1122.         
  1123.         case kLeftArrow_Key:
  1124.         case kRightArrow_Key:
  1125.         case kUpArrow_Key:
  1126.         case kDownArrow_Key:
  1127.             if (viewer->trackMouse &&
  1128.                 !EmptyRect(&viewer->marqueeRect) &&
  1129.                 !CmdKeyDown()) {
  1130.                 _KeyDownPICSViewerSelection(viewer, keyPressed);
  1131.             }
  1132.             else {
  1133.                 switch(keyPressed) {
  1134.                     case kLeftArrow_Key: _StepBackPICSViewer(viewer); break;
  1135.                     case kRightArrow_Key: _StepForwardPICSViewer(viewer); break;
  1136.                     case kUpArrow_Key: _RewindPICSViewer(viewer); break;
  1137.                     case kDownArrow_Key: _ForwardPICSViewer(viewer); break;
  1138.                 }
  1139.             }
  1140.         break;
  1141.     } // END keyPressed
  1142. } // END _KeyDownPICSViewer
  1143.  
  1144. // ---------------------------------------------------------------------------
  1145.  
  1146. void _UpdatePICSViewer(PICSViewerData *viewer) {
  1147.     GrafPtr savePort;
  1148.     short i;
  1149.     Str15 tempStr;
  1150.     Rect frame;
  1151.     short oldRefNum;
  1152.     
  1153.     oldRefNum = CurResFile();
  1154.     UseResFile(sPICSViewer.appFileRefNum);
  1155.  
  1156.     GetPort(&savePort);
  1157.     SetPort(viewer->window);
  1158.  
  1159.     // Draw speed box as a popup menu control
  1160.     GetDItemRect(viewer->window, kPICSViewer_SpeedBox, &frame);
  1161.     OffsetRect(&frame, 1, 1);
  1162.     frame.left++;
  1163.     frame.top++;
  1164.     FrameRect(&frame);
  1165.     frame.top--;
  1166.     frame.left--;
  1167.     OffsetRect(&frame, -1, -1);
  1168.     EraseRect(&frame);
  1169.     FrameRect(&frame);
  1170.  
  1171.     // Update current frame, speed, etc.
  1172.     _UpdatePICSViewerInfo(viewer);
  1173.     NumToString(viewer->picsFrame.right, tempStr);
  1174.     SetDItemText(viewer->window, kPICSViewer_PICSWidthField, tempStr);
  1175.     NumToString(viewer->picsFrame.bottom, tempStr);
  1176.     SetDItemText(viewer->window, kPICSViewer_PICSHeightField, tempStr);
  1177.  
  1178.     // Background picture of control buttons
  1179.     PicHandle btnPic;
  1180.     GetDItemRect(viewer->window, kPICSViewer_BtnBox, &frame);
  1181.     btnPic = (PicHandle)Get1Resource('PICT', kPICSViewer_ButtonBoxID);
  1182.     DrawPicture(btnPic, &frame);
  1183.     ReleaseResource((Handle)btnPic);
  1184.     
  1185.     // Speed popup menu triangle icon
  1186.     GetDItemRect(viewer->window, kPICSViewer_SpeedPopupBtn, &frame);
  1187.     btnPic = (PicHandle)Get1Resource('PICT', kPICSViewer_SpeedPopupID);
  1188.     DrawPicture(btnPic, &frame);
  1189.     ReleaseResource((Handle)btnPic);
  1190.  
  1191.     for (i = kPICSViewer_StopBtn; i <= kPICSViewer_ForwardBtn; i++) {
  1192.         _DrawPICSViewerBtn(viewer, i, false);
  1193.     }
  1194.     
  1195.     /*
  1196.     // Fill and frame pics play area
  1197.     RGBColor fillColor = {56797,56797,56797};
  1198.     RGBColor saveFore;
  1199.     GetForeColor(&saveFore);
  1200.     RGBForeColor(&fillColor);
  1201.     GetDItemRect(viewer->window, kPICSViewer_OutputBox, &frame);
  1202.     PaintRect(&frame);
  1203.  
  1204.     frame.left++;
  1205.     frame.top++;
  1206.     frame.right -= 2;
  1207.     frame.bottom -= 2;
  1208.     fillColor.red = fillColor.green = fillColor.blue = 43690;
  1209.     RGBForeColor(&fillColor);
  1210.     // Top
  1211.     MoveTo(frame.left, frame.top);
  1212.     LineTo(frame.right, frame.top);
  1213.     // Left
  1214.     MoveTo(frame.left, frame.top);
  1215.     LineTo(frame.left, frame.bottom);
  1216.     // Top
  1217.     MoveTo(frame.left, frame.top+1);
  1218.     LineTo(frame.right, frame.top+1);
  1219.     // Left
  1220.     MoveTo(frame.left+1, frame.top);
  1221.     LineTo(frame.left+1, frame.bottom);
  1222.     
  1223.     fillColor.red = fillColor.green = fillColor.blue = 65535;
  1224.     RGBForeColor(&fillColor);
  1225.     // Right
  1226.     MoveTo(frame.right, frame.top+1);
  1227.     LineTo(frame.right, frame.bottom);
  1228.     // Bottom
  1229.     MoveTo(frame.left+1, frame.bottom);
  1230.     MoveTo(frame.right, frame.bottom);
  1231.     // Right
  1232.     MoveTo(frame.right-1, frame.top+1);
  1233.     LineTo(frame.right-1, frame.bottom);
  1234.     // Bottom
  1235.     MoveTo(frame.left+1, frame.bottom-1);
  1236.     LineTo(frame.right, frame.bottom-1);
  1237.  
  1238.     frame.left--;
  1239.     frame.top--;
  1240.     frame.right += 2;
  1241.     frame.bottom += 2;
  1242.     fillColor.red = fillColor.green = fillColor.blue = 17476;
  1243.     RGBForeColor(&fillColor);
  1244.     FrameRect(&frame);
  1245.     */
  1246.     
  1247.     GetDItemRect(viewer->window, kPICSViewer_OutputBox, &frame);
  1248.     GrayDrawShadowBox(&frame, NULL);
  1249.  
  1250.     // Draw lines (dark gray)
  1251.     for (i = kPICSViewer_Line1; i <= kPICSViewer_Line3; i++) {
  1252.         GetDItemRect(viewer->window, i, &frame);
  1253.         GrayDrawShadowLine(&frame, NULL);
  1254.     }
  1255.  
  1256.     /*
  1257.     // Restore original fore color (black)
  1258.     RGBForeColor(&saveFore);
  1259.     */
  1260.  
  1261.     // Frame border of pics
  1262.     frame = viewer->outputFrame;
  1263.     InsetRect(&frame, -1, -1);
  1264.     GrayDrawShadowBox(&frame, NULL);
  1265.     
  1266.     // Draw PICS frame
  1267.     _DrawPICS(viewer);
  1268.     //_FrameMarquee(viewer);
  1269.  
  1270.     SetPort(savePort);
  1271.     UseResFile(oldRefNum);
  1272. } // END UpdatePICSViewer
  1273.  
  1274. // ---------------------------------------------------------------------------
  1275.  
  1276. void _ActivatePICSViewer(PICSViewerData *viewer, Boolean activate) {
  1277.     GrafPtr savePort;
  1278.     GetPort(&savePort);
  1279.     
  1280.     SetPort(viewer->window);
  1281.     if (!activate)
  1282.         _EraseMarquee(viewer, NULL);
  1283.     
  1284.     short hiliteMode;
  1285.     if (activate)
  1286.         hiliteMode = 0;
  1287.     else
  1288.         hiliteMode = 255;
  1289.     
  1290.     HiliteControl((ControlHandle)GetDItemHdl(viewer->window,
  1291.         kPICSViewer_CompositeBtn), hiliteMode);
  1292.     HiliteControl((ControlHandle)GetDItemHdl(viewer->window,
  1293.         kPICSViewer_SplitBtn), hiliteMode);
  1294.     HiliteControl((ControlHandle)GetDItemHdl(viewer->window,
  1295.         kPICSViewer_TrackMouseBtn), hiliteMode);
  1296.     HiliteControl((ControlHandle)GetDItemHdl(viewer->window,
  1297.         kPICSViewer_LoopBtn), hiliteMode);
  1298.     HiliteControl((ControlHandle)GetDItemHdl(viewer->window,
  1299.         kPICSViewer_BounceBackBtn), hiliteMode);
  1300.  
  1301.     if (!activate) {
  1302.         TextMode(grayishTextOr);
  1303.         UpdateDialog(viewer->window, viewer->window->visRgn);
  1304.     }
  1305.     else {
  1306.         TextMode(srcOr);
  1307.         UpdateDialog(viewer->window, viewer->window->visRgn);
  1308.     }
  1309.     SetPort(savePort);
  1310. } // END _ActivatePICSViewer
  1311.  
  1312. // ---------------------------------------------------------------------------
  1313.  
  1314. void _UpdatePICSViewerInfo(PICSViewerData *viewer) {
  1315.     Str15 tempStr;
  1316.  
  1317.     SetPort(viewer->window);
  1318.     NumToString(viewer->curFrame+1, tempStr);
  1319.     SetDItemText(viewer->window, kPICSViewer_CurFrameField, tempStr);
  1320.     NumToString(viewer->numFrames, tempStr);
  1321.     SetDItemText(viewer->window, kPICSViewer_NumFramesField, tempStr);
  1322.     TextFace(bold);
  1323.     NumToString(viewer->picsInfo.speed, tempStr);
  1324.     SetDItemText(viewer->window, kPICSViewer_SpeedField, tempStr);
  1325.     TextFace(0);
  1326. } // END _UpdatePICSViewerInfo
  1327.  
  1328. // ---------------------------------------------------------------------------
  1329.  
  1330. static void _TrackMouseIdle(PICSViewerData *viewer, Point mouseLoc);
  1331.  
  1332. void _IdlePICSViewer(PICSViewerData *viewer) {
  1333.     GrafPtr savePort;
  1334.     
  1335.     GetPort(&savePort);
  1336.     SetPort(viewer->window);
  1337.  
  1338.     if (FrontWindow() == viewer->window) {        
  1339.         if (viewer->trackMouse) {
  1340.             // Update cursor
  1341.             Point mouseLoc;
  1342.             GetMouse(&mouseLoc);
  1343.  
  1344.             if (!PtInRect(mouseLoc, &viewer->outputFrame)) {
  1345.                 SetCursor(&qd.arrow);
  1346.                 viewer->currentAction = kNoAction;
  1347.  
  1348.                 if (viewer->trackingPoint.h != 0 &&
  1349.                     viewer->trackingPoint.v != 0) {
  1350.                     SetDItemText(viewer->window,
  1351.                         kPICSViewer_TrackMouseField, kEmptyStr);
  1352.                     viewer->trackingPoint.h = 0;
  1353.                     viewer->trackingPoint.v = 0;
  1354.                 }
  1355.             }
  1356.             else {
  1357.                 _SetPICSViewerCursor(viewer);
  1358.                 _TrackMouseIdle(viewer, mouseLoc);
  1359.             }
  1360.  
  1361.             // Animate current marquee. Cool!
  1362.             if (!EmptyRect(&viewer->marqueeRect)) {
  1363.                 _FrameMarquee(viewer);
  1364.             }
  1365.         }
  1366.     }
  1367.     else {
  1368.     }
  1369.  
  1370.     SetPort(savePort);
  1371. } // END _IdlePICSViewer
  1372.  
  1373. void _TrackMouseIdle(PICSViewerData *viewer, Point mouseLoc) {
  1374.     Str63 outputStr    = "\p[ Left: ";
  1375.     Str15 topStr    = "\p | Top: ";
  1376.     Str15 endStr    = "\p ]";
  1377.     Str15 numStr;
  1378.     
  1379.     if (!EqualPt(mouseLoc, viewer->trackingPoint)) {
  1380.         viewer->trackingPoint = mouseLoc;
  1381.  
  1382.         mouseLoc.h -= viewer->outputFrame.left;
  1383.         mouseLoc.v -= viewer->outputFrame.top;
  1384.  
  1385.         NumToString(mouseLoc.h, numStr);
  1386.         PStrCat(numStr, outputStr);
  1387.         PStrCat(topStr, outputStr);
  1388.         NumToString(mouseLoc.v, numStr);
  1389.         PStrCat(numStr, outputStr);
  1390.         PStrCat(endStr, outputStr);
  1391.         
  1392.         SetDItemText(viewer->window, kPICSViewer_TrackMouseField, outputStr);
  1393.     }
  1394. } // END _TrackMouseIdle
  1395.  
  1396. // ---------------------------------------------------------------------------
  1397.  
  1398. enum {
  1399.     kAdjustSlackPixels = 3
  1400. };
  1401.  
  1402. void _SetPICSViewerCursor(PICSViewerData *viewer) {
  1403.     Point mouseLoc;
  1404.     GetMouse(&mouseLoc);
  1405.  
  1406.     if (viewer->trackMouse && !EmptyRect(&viewer->marqueeRect)) {
  1407.         Rect prelimCheck = viewer->marqueeRect;
  1408.         InsetRect(&prelimCheck, -kAdjustSlackPixels,
  1409.             -kAdjustSlackPixels);
  1410.  
  1411.         if (PtInRect(mouseLoc, &prelimCheck)) {
  1412.             // Determine whether to adjust selection or not
  1413.             Rect marquee = viewer->marqueeRect;
  1414.     
  1415.             if ((mouseLoc.h < marquee.left + kAdjustSlackPixels) &&
  1416.                 (mouseLoc.h > marquee.left - kAdjustSlackPixels)) {
  1417.                 SetCursor(*GetCursor(kHorizontalArrowID));
  1418.                 viewer->currentAction = kLeftAdjustSelectionAction;
  1419.             }
  1420.             else if ((mouseLoc.h < marquee.right + kAdjustSlackPixels) &&
  1421.                 (mouseLoc.h > marquee.right - kAdjustSlackPixels)) {
  1422.                 SetCursor(*GetCursor(kHorizontalArrowID));
  1423.                 viewer->currentAction = kRightAdjustSelectionAction;
  1424.             }
  1425.             else if ((mouseLoc.v < marquee.top + kAdjustSlackPixels) &&
  1426.                 (mouseLoc.v > marquee.top - kAdjustSlackPixels)) {
  1427.                 SetCursor(*GetCursor(kVerticalArrowID));
  1428.                 viewer->currentAction = kTopAdjustSelectionAction;
  1429.             }
  1430.             else if ((mouseLoc.v < marquee.bottom + kAdjustSlackPixels) &&
  1431.                 (mouseLoc.v > marquee.bottom - kAdjustSlackPixels)) {
  1432.                 SetCursor(*GetCursor(kVerticalArrowID));
  1433.                 viewer->currentAction = kBottomAdjustSelectionAction;
  1434.             }
  1435.  
  1436.             else if (PtInRect(mouseLoc, &viewer->marqueeRect)) {
  1437.                 /*
  1438.                 if (CROPCLICK_SHORTCUT) {
  1439.                     SetCursor(*GetCursor(kScissorCursorID));
  1440.                     viewer->currentAction = kCompositeAction;
  1441.                 }
  1442.                 */
  1443.                 if (MOVESELECTION_SHORTCUT) {
  1444.                     SetCursor(*GetCursor(kHandOpenCursorID));
  1445.                     viewer->currentAction = kMoveSelectionAction;
  1446.                 }
  1447.                 else {
  1448.                     SetCursor(*GetCursor(kCrossCursorID));
  1449.                     viewer->currentAction = kSelectionAction;
  1450.                 }
  1451.             }
  1452.             
  1453.             else {
  1454.                 SetCursor(*GetCursor(kCrossCursorID));
  1455.                 viewer->currentAction = kSelectionAction;
  1456.             }
  1457.         }
  1458.         else {
  1459.             SetCursor(*GetCursor(kCrossCursorID));
  1460.             viewer->currentAction = kSelectionAction;
  1461.         }
  1462.     }
  1463.     else {
  1464.         if (PtInRect(mouseLoc, &viewer->outputFrame)) {
  1465.             SetCursor(*GetCursor(kCrossCursorID));
  1466.             viewer->currentAction = kSelectionAction;
  1467.         }
  1468.         else {
  1469.             SetCursor(&qd.arrow);
  1470.             viewer->currentAction = kNoAction;
  1471.         }
  1472.     }
  1473. } // END _SetPICSViewerCursor
  1474.  
  1475. // ---------------------------------------------------------------------------
  1476.  
  1477. void _CompositePICSViewer(PICSViewerData *viewer) {
  1478.     FSSpec file;
  1479.     Rect cropRect;
  1480.     file = viewer->picsFile;
  1481.  
  1482.     if (viewer->trackMouse && !EmptyRect(&viewer->marqueeRect)) {
  1483.         cropRect = viewer->marqueeRect;
  1484.         OffsetRect(&cropRect, -viewer->outputFrame.left,
  1485.             -viewer->outputFrame.top);
  1486.     }
  1487.     else {
  1488.         cropRect = viewer->outputFrame;
  1489.         FlushRectTopLeft(&cropRect);
  1490.     }
  1491.  
  1492.     SetupCompositeDialog(&viewer->picsFile, viewer->fileRefNum, &cropRect,
  1493.         viewer->numFrames);
  1494. } // END _CompositePICSViewer
  1495.  
  1496. void _SplitPICSViewer(PICSViewerData *viewer) {
  1497.     SplitPICSFile(kPhotoShopCreatorType, &viewer->picsFile, viewer->fileRefNum);
  1498. } // END _SplitPICSViewer
  1499.  
  1500. // ---------------------------------------------------------------------------
  1501.  
  1502.  
  1503. void _CopyPICSViewerFrame(PICSViewerData *viewer) {
  1504.     PicHandle picToCopy;
  1505.  
  1506.     if (!viewer->trackMouse || EmptyRect(&viewer->marqueeRect)) {
  1507.         // If no marquee selection, copy direct from original
  1508.         // pict resource
  1509.         UseResFile(viewer->fileRefNum);
  1510.         picToCopy = (PicHandle)Get1Resource(kPICSRsrcType,
  1511.             kPICSRsrcStartID + viewer->curFrame);
  1512.         if (picToCopy == NULL) {
  1513.             // ERROR HANDLING
  1514.             SysBeep(10);
  1515.             return;
  1516.         }
  1517.         UseResFile(sPICSViewer.appFileRefNum);
  1518.         DetachResource((Handle)picToCopy);
  1519.     }
  1520.     else {
  1521.         // Else copy only a portion of image by capturing
  1522.         // from offscreen buffer. Result may differ from
  1523.         // above because depth of buffer may be different
  1524.         // from depth of original PICT resource
  1525.         Rect srcRect = viewer->marqueeRect;
  1526.  
  1527.         OffsetRect(&srcRect, -viewer->outputFrame.left,
  1528.             -viewer->outputFrame.top);
  1529.         // Map src rect to correct frame in viewer->buffer
  1530.         OffsetRect(&srcRect, viewer->curFrame *
  1531.             viewer->picsFrame.right, 0);
  1532.             
  1533.         CapturePICT(viewer->buffer, viewer->buffer,
  1534.             &srcRect, &srcRect, &picToCopy);
  1535.     }
  1536.  
  1537.     if (picToCopy != NULL) {
  1538.         ZeroScrap();
  1539.         HLock((Handle)picToCopy);
  1540.         PutScrap(GetHandleSize((Handle)picToCopy),
  1541.             kPICSRsrcType, (Ptr)*picToCopy);
  1542.         HUnlock((Handle)picToCopy);
  1543.         DisposeHandle((Handle)picToCopy);
  1544.     }
  1545.     else {
  1546.         // ERROR HANDLING
  1547.         SysBeep(10);
  1548.     }
  1549. } // END 
  1550.  
  1551. // ---------------------------------------------------------------------------
  1552.  
  1553. void _RewindPICSViewer(PICSViewerData *viewer) {
  1554.     viewer->curFrame = 0;
  1555.     _DrawPICS(viewer);
  1556. } // END _RewindPICSViewer
  1557.  
  1558. void _StepBackPICSViewer(PICSViewerData *viewer) {
  1559.     if (viewer->curFrame != 0)
  1560.         viewer->curFrame--;
  1561.     _DrawPICS(viewer);
  1562. } // END _StepBackPICSViewer
  1563.  
  1564. void _StepForwardPICSViewer(PICSViewerData *viewer) {
  1565.     if (viewer->curFrame !=
  1566.         viewer->numFrames-1)
  1567.         viewer->curFrame++;
  1568.     _DrawPICS(viewer);
  1569. } // END _StepForwardPICSViewer
  1570.  
  1571. void _ForwardPICSViewer(PICSViewerData *viewer) {
  1572.     viewer->curFrame = viewer->numFrames-1;
  1573.     _DrawPICS(viewer);
  1574. } // END _ForwardPICSViewer
  1575.  
  1576. // ---------------------------------------------------------------------------
  1577.  
  1578. void _IncreasePICSViewerSpeed(PICSViewerData *viewer) {
  1579.     short speedIncrement;
  1580.  
  1581.     if (!LARGERINCREMENT_SHORTCUT)
  1582.         speedIncrement = kUsualSpeedIncrement;
  1583.     else
  1584.         speedIncrement = kLargerSpeedIncrement;
  1585.  
  1586.     viewer->picsInfo.speed += speedIncrement;
  1587.  
  1588.     _UpdatePICSViewerInfo(viewer);
  1589.     _SetPICSViewerModified(viewer, true);
  1590. } // END _IncreasePICSViewerSpeed
  1591.  
  1592. void _DecreasePICSViewerSpeed(PICSViewerData *viewer) {
  1593.     short speedIncrement;
  1594.  
  1595.     if (!LARGERINCREMENT_SHORTCUT)
  1596.         speedIncrement = kUsualSpeedIncrement;
  1597.     else
  1598.         speedIncrement = kLargerSpeedIncrement;
  1599.  
  1600.     if (viewer->picsInfo.speed > speedIncrement) {
  1601.         viewer->picsInfo.speed -= speedIncrement;
  1602.         _UpdatePICSViewerInfo(viewer);
  1603.         _SetPICSViewerModified(viewer, true);
  1604.     }
  1605.     else {
  1606.         SysBeep(10);
  1607.     }
  1608. } // END _DescreasePICSViewerSpeed
  1609.  
  1610. // ---------------------------------------------------------------------------
  1611.  
  1612. void _SetPICSViewerModified(PICSViewerData *viewer, Boolean modified) {
  1613.     Str255 windName;
  1614.     Str255 updatedName = "\p√";
  1615.     
  1616.     if (viewer->fileModified != modified) {
  1617.         viewer->fileModified = modified;
  1618.     
  1619.         if (modified) {
  1620.             GetWTitle(viewer->window, windName);
  1621.             PStrCat(windName, updatedName);
  1622.             SetWTitle(viewer->window, updatedName);
  1623.         }
  1624.         else {
  1625.             GetWTitle(viewer->window, windName);
  1626.             windName[1] = windName[0] - 1;
  1627.             SetWTitle(viewer->window, &windName[1]);
  1628.         }
  1629.     }
  1630. } // END _SetPICSViewerModified
  1631.  
  1632. Boolean _IsPICSViewerModified(PICSViewerData *viewer) {    
  1633.     return(viewer->fileModified);
  1634. } // END _IsPICSViewerModified
  1635.  
  1636. // ---------------------------------------------------------------------------
  1637.  
  1638. #include "AlphaChannel.h"
  1639.  
  1640. void _DrawPICS(PICSViewerData *viewer) {
  1641.     Rect srcFrame, destFrame;
  1642.     short transferMode;
  1643.     RGBColor saveBack, backColor;
  1644.  
  1645.     GetBackColor(&saveBack);
  1646.     backColor.red = backColor.green = backColor.blue = 0xFFFF;    // White
  1647.     RGBBackColor(&backColor);
  1648.  
  1649.     transferMode = GetGraphicsBufferTransferMode();
  1650.     SetGraphicsBufferTransferMode(srcCopy);
  1651.  
  1652.     srcFrame = viewer->picsFrame;
  1653.     OffsetRect(&srcFrame, 0, viewer->curFrame * srcFrame.bottom);
  1654.     destFrame = viewer->outputFrame;
  1655.  
  1656.     // An experiment: this will allow you to see the alpha channel
  1657.     // of a 32-bit PICS file. You will not be able to save it yet.
  1658.     // Actually, you won't see the alpha channel. Instead, the sprite
  1659.     // will be masked, and the background will be erased with white.
  1660.     // Full alpha-channel support will be in the next release.
  1661.     if (OptionKeyDown()) {
  1662.         SplitAlphaChannel(
  1663.             GetGraphicsBufferGWorld(viewer->buffer),
  1664.             GetGraphicsBufferGWorld(viewer->buffer),
  1665.             NULL,
  1666.             NULL);
  1667.     }
  1668.  
  1669.     CopyGraphicsBuffer2Window(viewer->buffer,
  1670.         viewer->window, (CP_Rect*)&srcFrame, (CP_Rect*)&destFrame);
  1671.     
  1672.     SetGraphicsBufferTransferMode(transferMode);
  1673.     RGBBackColor(&saveBack);
  1674.  
  1675.     Str15 tempStr;
  1676.     NumToString(viewer->curFrame+1, tempStr);
  1677.     SetDItemText(viewer->window, kPICSViewer_CurFrameField, tempStr);
  1678. } // END DrawPICS
  1679.  
  1680. // ---------------------------------------------------------------------------
  1681.  
  1682.  
  1683. void _PlaySynchPICSViewer(PICSViewerData *viewer) {
  1684.     long dummy;
  1685.     Boolean done = false;
  1686.     Boolean increasingFrame = true;
  1687.     Boolean drawMarquee;
  1688.  
  1689.     _DrawPICSViewerBtn(viewer, kPICSViewer_PlayBtn, true);
  1690.     while(StillDown()) {} // Wait for a mouse-up first.
  1691.  
  1692.     if (DRAWMARQUEE_SHORTCUT && !EmptyRect(&viewer->marqueeRect))
  1693.         drawMarquee = true;
  1694.     else
  1695.         drawMarquee = false;
  1696.  
  1697.     if (FASTESTANIM_SHORTCUT)
  1698.         SetTimerRate(kTimerRateOff);
  1699.     else
  1700.         StartTimerFPS(viewer->picsInfo.speed);
  1701.  
  1702.     do {
  1703.         if (TimerDoNow()) {
  1704.             _DrawPICS(viewer);
  1705.             if (drawMarquee)
  1706.                 _FrameMarquee(viewer);
  1707.  
  1708.             if (viewer->doLoop) {
  1709.                 if (!viewer->doBounceBack) {
  1710.                     if (viewer->curFrame <
  1711.                         viewer->numFrames-1)
  1712.                         viewer->curFrame++;
  1713.                     else
  1714.                         viewer->curFrame = 0;
  1715.                 }
  1716.                 else {
  1717.                     if (increasingFrame) {
  1718.                         if (viewer->curFrame < viewer->numFrames-1)
  1719.                             viewer->curFrame++;
  1720.                         else {
  1721.                             increasingFrame = false;
  1722.                             viewer->curFrame--;
  1723.                         }
  1724.                     }
  1725.                     else {
  1726.                         if (viewer->curFrame > 0)
  1727.                             viewer->curFrame--;
  1728.                         else {
  1729.                             increasingFrame = true;
  1730.                             viewer->curFrame++;
  1731.                         }
  1732.                     }
  1733.                 }
  1734.             }
  1735.             else {
  1736.                 if (viewer->curFrame == viewer->numFrames-1)
  1737.                     done = true;
  1738.                 else
  1739.                     viewer->curFrame++;
  1740.             }
  1741.             ResetTimer();
  1742.         }
  1743.  
  1744.         if (Button())
  1745.             done = true;
  1746.     } while (!done);
  1747.     _DrawPICS(viewer);    // Update one last time to synch curFrame field in
  1748.                         // dlog and current frame in viewer->curFrame
  1749.     StopTimer();
  1750.  
  1751.     _DrawPICSViewerBtn(viewer, kPICSViewer_StopBtn, true);
  1752.     Delay(10, &dummy);
  1753.     _DrawPICSViewerBtn(viewer, kPICSViewer_StopBtn, false);
  1754.     _DrawPICSViewerBtn(viewer, kPICSViewer_PlayBtn, false);
  1755.     
  1756.     FlushEvents(everyEvent, 0);
  1757. } // ENd PlaySynchPICSViewer